#ifndef SNCNEIGHBOR_CC
#define SNCNEIGHBOR_CC 1

#include "snc.hh"
#include "sncneighbor.hh"
#include <click/config.h>


CLICK_DECLS


//get the pkt with appropriate length
Packet* SNCNeighbor::get_encode_pkt(const Packet* p){
	Packet* p_out=0;
	if(!_waitting_ack_pktq.empty()){
		return *_waitting_ack_pktq.front();
	}
	else{
		if(p->length()>=_threhold && !_long_pktq.empty()){
				p_out=*_long_pktq.front();
				_waitting_ack_pktq.push_back(_long_pktq.front());
				_long_pktq.pop_front();
				return p_out;
		}
		if(!_short_pktq.empty()){
			p_out=*_short_pktq.front();
			_waitting_ack_pktq.push_back(_short_pktq.front());
			_short_pktq.pop_front();
			return p_out;
		}
	}
	return p_out;
}

bool SNCNeighbor::encode_check(Natives& natives){
	Natives::iterator iter;
	for(iter=natives.begin();iter!=natives.end();++iter){
		uint32_t pkt_id=MAKE_PKT_ID((*iter)->ip_header()->ip_src.s_addr,(*iter)->ip_header()->ip_id);
		if(get_pkt_credit(pkt_id)<0.7)
			return false;
	}
	Packet* p=get_encode_pkt(natives.front());
	if(!p){
		natives.push_back(p);
		return true;
	}
	else
		return false;
}

void SNCNeighbor::received_ack(uint16_t last_ack,bitmap_sizetype bitmap){
	Packet_list_v::iterator iter;
	uint16_t seqno=last_ack-sizeof(bitmap_sizetype)*byte_size-1;
	bitmap_sizetype bittest=1;
	bittest<<=sizeof(bitmap_sizetype)*byte_size-1;
	while(bittest){
		if((bittest & bitmap)!=0){
			for(iter=_waitting_ack_pktq.begin();iter!=_waitting_ack_pktq.end();++iter){
				if((**iter)->ip_header()->ip_id==seqno){
					remove_pkt(&*iter);
					break;
				}
			}
		}
		bittest>>=1;
		seqno++;
	}
	//last_ack
	for(iter=_waitting_ack_pktq.begin();iter!=_waitting_ack_pktq.end();++iter){
		if((**iter)->ip_header()->ip_id==seqno){
			remove_pkt(&*iter);
			return;
		}
	}

}

SNCNeighbor* SNCNeighborManag::create_neighbor(const uint8_t* mac,uint16_t pkt_seqno){
		SNCNeighbor* neighbor=new SNCNeighbor(mac,_snc->_poolcap,&_snc->_sendq,_snc->_pkt_threshold,pkt_seqno);
		_neighbors.push_back(neighbor);
		return neighbor;
	}

void SNCNeighborManag::codepoll(Natives& natives){
	Vector<SNCNeighbor* >::iterator iter;
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		(*iter)->encode_check(natives);
	}

}

bool SNCNeighborManag::add_to_sendq(const uint8_t* dst,Packet* p){
	Vector<SNCNeighbor* >::iterator iter;
	//find the neighbor whom we send this pkt to
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		if(*(*iter)==dst){
			return (*iter)->add_pkt(p);
		}
	}
	SNCNeighbor* neighbor=create_neighbor(dst);
	return neighbor->add_pkt(p);
}

void SNCNeighborManag::remove_from_sendq(const uint8_t* dst,Packet_v p_v ){
	Vector<SNCNeighbor* >::iterator iter;
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		if(*(*iter)==dst){
			(*iter)->remove_pkt(&p_v);
			return;
		}
	}
	(*p_v)->kill();
	_snc->_sendq.erase(p_v);
}

void SNCNeighborManag::update_ackqueue(const uint8_t* src,ack_header* ack_data,uint32_t size){
	//first find the ack_header for me
	for(unsigned i=0;i<size;i++){
		if(memcpy(_snc->_eth.data(),&ack_data[i]._neighbor,6)==0){
			//find the neighbor who send the ack
			for(unsigned j=0;j<_neighbors.size();j++){
				if(*_neighbors[j]==src){
					_neighbors[j]->received_ack(ack_data[i]._last_ack,ack_data[i]._bit_map);
				}
			}
			return;
		}
	}
}

void SNCNeighborManag::update_pktpool(const uint8_t* mac,report_header* data,uint32_t size){
	for(uint32_t i=0;i<size;i++){
		bitmap_sizetype bitmap=data[i]._bit_map;
		uint16_t seqno=data[i]._last_pkt;
		uint32_t src=data[i]._src_ip;
		Vector<SNCNeighbor* >::iterator iter;
		//find the neighbor who send the report
		for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
			if(*(*iter)==mac){
				//store the LAST_PKT
				(*iter)->update_pool(MAKE_PKT_ID(src,seqno));
				while(bitmap){
					seqno--;
					if(bitmap & 0x01){
						(*iter)->update_pool(MAKE_PKT_ID(src,seqno));
					}
					bitmap>>=1;
				}
				break;
				//goto next;
			}
		}
		//can not find the neighbor,so we have to create it
		if(iter==_neighbors.end()){
			SNCNeighbor* neighbor=create_neighbor(mac);
			neighbor->update_pool(MAKE_PKT_ID(src,seqno));
			while(bitmap){
				seqno--;
				if(bitmap & 0x01){
					(*iter)->update_pool(MAKE_PKT_ID(src,seqno));
				}
				bitmap>>=1;
			}
		}

		//next:;
	}

}

void SNCNeighborManag::update_pktpool(const uint8_t* mac,encoded_header* data,uint32_t size){
	Vector<SNCNeighbor* >::iterator iter;
	//find the neighbor who sned the encoded_header
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		if(*(*iter)==mac){
			for(uint32_t i=0;i<size;i++){
				(*iter)->update_pool(data[i]._pkt_id);
			}
			return;
		}
	}
	//can not find the neighbor,so we have to create it
	SNCNeighbor* neighbor=create_neighbor(mac);
	for(uint32_t i=0;i<size;i++){
		neighbor->update_pool(data[i]._pkt_id);
	}
}

void SNCNeighborManag::update_pktpool(const uint8_t* mac,uint32_t pkt_id){
	Vector<SNCNeighbor* >::iterator iter;
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		if(*(*iter)==mac){
			(*iter)->update_pool(pkt_id);
			return;
		}
	}
	//can not find the neighbor,so we have to create it
	SNCNeighbor* neighbor=create_neighbor(mac);
	neighbor->update_pool(pkt_id);

}

uint16_t SNCNeighborManag::get_pkt_seqno(const uint8_t* mac){
	Vector<SNCNeighbor* >::iterator iter;
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		if(*(*iter)==mac){
			return (*iter)->get_seq();
		}
	}
	SNCNeighbor* neighbor=create_neighbor(mac);
	return neighbor->get_seq();
}

void SNCNeighborManag::set_poolcap(unsigned cap){
	Vector<SNCNeighbor* >::iterator iter;
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		(*iter)->set_poolcap(cap);
	}
}

void SNCNeighborManag::set_threshold(unsigned threshold){
	Vector<SNCNeighbor* >::iterator iter;
	for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
		(*iter)->set_threshold(threshold);
	}
}


/*
void SNCNeighborManag::update_sendq(const uint8_t* src,ack_header* ack_data,uint32_t size){
	Vector<SNCNeighbor* >::iterator iter;
	unsigned j;
	for(j=0;i<size;i++){
		if(memcmp(_snc->_eth.data(),&ack_data[i]._neighbor,6)==0){
			for(iter=_neighbors.begin();iter!=_neighbors.end();iter++){
				if(*(*iter)==src){
					return (*iter)->get_seq();
				}
			}

		}
	}


}
*/

CLICK_ENDDECLS
ELEMENT_PROVIDES(SNCNeighbor)
ELEMENT_PROVIDES(SNCNeighborManag)
#endif
